/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.generator.proxy;

/**
 *
 * @author Laurent WOUTERS
 */
public class ClassWriter {
    public static void write(String folder, xowl.generator.ClassGenerator cg, int signature) throws java.io.IOException {
        java.io.Writer writer = xowl.utils.Files.getWriter(folder + cg.getName() + ".java");
        writer.append("/* This file has been generated by:\n");
        writer.append("   xOWL Code Generator\n");
        writer.append("*/\n");
        writer.append("package " + cg.getPackage().getFullName() + ";\n");
        writer.append("import java.util.Collection;\n");
        writer.append("import xowl.interpreter.adapter.BPODatabase;\n");
        writer.append("import xowl.interpreter.adapter.ProxyObject;\n");
        writer.append("import xowl.interpreter.adapter.ProxyObjectGetter;\n");
        writer.append("import xowl.interpreter.adapter.BaseProxyObject;\n");
        writer.append("import xowl.interpreter.adapter.ClassObserver;\n");
        writer.append("import xowl.interpreter.adapter.ClassObserverLink;\n");
        writer.append("import xowl.interpreter.adapter.ObjectPropertyObserver;\n");
        writer.append("import xowl.interpreter.adapter.ObjectPropertyObserverLink;\n");
        writer.append("import xowl.interpreter.adapter.DataPropertyObserver;\n");
        writer.append("import xowl.interpreter.adapter.DataPropertyObserverLink;\n");
        writer.append("\n");

        if (!cg.isAbstract())
            writer.append("public class " + cg.getName() + " extends BaseProxyObject");
        else
            writer.append("public interface " + cg.getName() + " extends ProxyObject");
        
        // writes parency
        boolean first = true;
        for (xowl.generator.ClassGenerator parent : cg.getSuperClasses()) {
            if (first) {
                if (!cg.isAbstract()) writer.append(" implements ");
                else writer.append(", ");
            } else
                writer.append(", ");
            writer.append(parent.getJavaName());
            first = false;
        }
        writer.append(" {\n");

        if (cg.isAbstract())
            for (xowl.generator.PropertyInterface inter : cg.getPropertyInterfaces())
                PropInterWriter.write(writer, inter);
        else
            writeJava_Concrete(writer, cg, signature);
        
        writer.append("}\n");
        writer.close();
    }
    private static void writeJava_Concrete(java.io.Writer writer, xowl.generator.ClassGenerator cg, int signature) throws java.io.IOException {
        // writes all Implementations
        for (xowl.generator.PropertyImplementation implementation : cg.getPropertyImplementations()) {
            PropImplWriter.write(writer, implementation);
            writer.append("\n");
        }
        
        // writes all static instances
        for (xowl.generator.InstanceGenerator instance : cg.getStaticInstances()) {
            InstanceWriter.write(writer, instance);
            writer.append("\n");
        }
        
        // writes static data
        writer.append("    private static final String classIRI = \"" + cg.getOWLClass().getInterpretationOf().getHasIRI().getHasValue() + "\";\n");
        writer.append("    private static final java.util.Map<ClassObserver, ClassObserverLink> mapClassObservers = new java.util.HashMap<ClassObserver, ClassObserverLink>();\n");
        writer.append("    private static BPODatabase<" + cg.getName() + "> classDB = new BPODatabase<" + cg.getName() + ">();\n");
        writer.append("    public static String getClassIRI() { return classIRI; }\n");
        
        // static constructor
        writer.append("    static {\n");
        writer.append("        if (!activated.contains(" + Integer.toString(signature) +")) {\n");
        writer.append("            " + cg.getPackage().getModel().getBasePackage() + ".Activator.activate();\n");
        writer.append("            activated.add(" + Integer.toString(signature) +");\n");
        writer.append("        }\n");
        writer.append("    }\n");
        
        writer.append("    public static void register() {\n");
        writer.append("        mapIRI2Class.put(classIRI, " + cg.getName() + ".class);\n");
        writer.append("        mapIRI2Getter.put(classIRI, new ProxyObjectGetter() {\n");
        writer.append("            @Override public ProxyObject get(xowl.interpreter.Repository repository, xowl.lang.runtime.Entity entity) { return " + cg.getName() + ".get(repository, entity); }\n");
        writer.append("        });\n");
        writer.append("    }\n");
        
        // writes constructor
        writer.append("    private " + cg.getName() + "(xowl.interpreter.Repository repository, xowl.lang.runtime.Entity entity) {\n");
        writer.append("        super(repository, entity);\n");
        writer.append("    }\n");
        
        writer.append("    public void delete() {\n");
        writer.append("        removeAllObservers();\n");
        for (xowl.generator.PropertyImplementation impl : cg.getPropertyImplementations()) {
            String name = impl.getProperty().getName();
            if (impl.getProperty().isObjectProperty())
                writer.append("        removeAllObjects(getProperty_" + name + "());\n");
            else
                writer.append("        removeAllData(getProperty_" + name + "());\n");
        }
        writer.append("        xowl.lang.owl2.ClassAssertion axiom = new xowl.lang.owl2.ClassAssertion();\n");
        writer.append("        axiom.setClasse(repository.getIRI(classIRI));\n");
        writer.append("        axiom.setIndividual(entity.getHasIRI());\n");
        writer.append("        removeAxiom(axiom);\n");
        writer.append("        classDB.remove(this);\n");
        writer.append("        database.remove(this);\n");
        writer.append("    }\n");
        
        // writes getter
        writer.append("    public static " + cg.getName() + " get(xowl.interpreter.Repository repository, String iri) {\n");
        writer.append("        xowl.lang.runtime.Entity entity = repository.resolveEntity(iri);\n");
        writer.append("        return get(repository, entity);\n");
        writer.append("    }\n");
        
        writer.append("    public static " + cg.getName() + " get(xowl.interpreter.Repository repository, xowl.lang.runtime.Entity entity) {\n");
        writer.append("        " + cg.getName() + " value = classDB.get(entity);\n");
        writer.append("        if (value != null) return value;\n");
        writer.append("        value = new " + cg.getName() + "(repository, entity);\n");
        writer.append("        classDB.insert(value);\n");
        writer.append("        database.insert(value);\n");
        writer.append("        return value;\n");
        writer.append("    }\n");
        
        writer.append("    public static java.util.Collection<" + cg.getName() + "> getAll(xowl.interpreter.Repository repository) {\n");
        writer.append("        java.util.List<" + cg.getName() + "> values = new java.util.ArrayList<" + cg.getName() + ">();\n");
        writer.append("        xowl.lang.owl2.IRI iri = repository.getIRI(classIRI);\n");
        writer.append("        xowl.lang.actions.QueryVariable var = new xowl.lang.actions.QueryVariable();\n");
        writer.append("        var.setName(\"v\");\n");
        writer.append("        xowl.lang.owl2.ClassAssertion axiom = new xowl.lang.owl2.ClassAssertion();\n");
        writer.append("        axiom.setIndividual(var);\n");
        writer.append("        axiom.setClasse(iri);\n");
        writer.append("        xowl.lang.actions.ValuesOf query = new xowl.lang.actions.ValuesOf();\n");
        writer.append("        query.addAxioms(axiom);\n");
        writer.append("        query.setOn(var);\n");
        writer.append("        try {\n");
        writer.append("            Collection<xowl.interpreter.QuerySolution> solutions = repository.select(query);\n");
        writer.append("            for (xowl.interpreter.QuerySolution solution : solutions)\n");
        writer.append("                values.add(get(repository, (xowl.lang.runtime.Entity)solution.getValue(var)));\n");
        writer.append("        } catch (xowl.interpreter.XOWLException ex) { }\n");
        writer.append("        return values;\n");
        writer.append("    }\n");
        writer.append("    public static java.util.Collection<" + cg.getName() + "> getAll(xowl.interpreter.Repository repository, xowl.lang.owl2.Ontology ontology) {\n");
        writer.append("        java.util.List<" + cg.getName() + "> values = new java.util.ArrayList<" + cg.getName() + ">();\n");
        writer.append("        xowl.lang.owl2.IRI iri = repository.getIRI(classIRI);\n");
        writer.append("        xowl.lang.actions.QueryVariable var = new xowl.lang.actions.QueryVariable();\n");
        writer.append("        var.setName(\"v\");\n");
        writer.append("        xowl.lang.owl2.ClassAssertion axiom = new xowl.lang.owl2.ClassAssertion();\n");
        writer.append("        axiom.setIndividual(var);\n");
        writer.append("        axiom.setClasse(iri);\n");
        writer.append("        xowl.lang.actions.ValuesOf query = new xowl.lang.actions.ValuesOf();\n");
        writer.append("        query.addAxioms(axiom);\n");
        writer.append("        query.setOn(var);\n");
        writer.append("        try {\n");
        writer.append("            Collection<xowl.interpreter.QuerySolution> solutions = repository.select(query);\n");
        writer.append("            for (xowl.interpreter.QuerySolution solution : solutions) {\n");
        writer.append("                xowl.lang.runtime.Entity entity = (xowl.lang.runtime.Entity)solution.getValue(var);\n");
        writer.append("                if (entity.getContainedBy() == ontology)\n");
        writer.append("                    values.add(get(repository, entity));\n");
        writer.append("            }\n");
        writer.append("        } catch (xowl.interpreter.XOWLException ex) { }\n");
        writer.append("        return values;\n");
        writer.append("    }\n");
        
        // writes instantiator
        writer.append("    public static " + cg.getName() + " instantiate(xowl.interpreter.Repository repository, xowl.lang.owl2.Ontology ontology) {\n");
        writer.append("        String iri = ontology.getHasIRI().getHasValue() + \"#\" + generateID();\n");
        writer.append("        xowl.lang.runtime.Entity entity = repository.resolveEntity(iri);\n");
        writer.append("        xowl.lang.owl2.ClassAssertion axiom = new xowl.lang.owl2.ClassAssertion();\n");
        writer.append("        axiom.setClasse(repository.getIRI(classIRI));\n");
        writer.append("        axiom.setIndividual(entity.getHasIRI());\n");
        writer.append("        try { repository.apply(new xowl.interpreter.Change(ontology, axiom, true)); }\n");
        writer.append("        catch (xowl.interpreter.XOWLException ex) { }\n");
        writer.append("        " + cg.getName() + " value = new " + cg.getName() + "(repository, entity);\n");
        writer.append("        classDB.insert(value);\n");
        writer.append("        database.insert(value);\n");
        writer.append("        return value;\n");
        writer.append("    }\n");
        
        writer.append("    public static void addObserver(xowl.interpreter.Repository repository, ClassObserver<" + cg.getName() + "> observer) {\n");
        writer.append("        xowl.lang.runtime.Class _class = repository.interpretAsClass(repository.resolveEntity(classIRI));\n");
        writer.append("        ClassObserverLink link = new ClassObserverLink(_class, repository, " + cg.getName() + ".class, observer);\n");
        writer.append("        mapClassObservers.put(observer, link);\n");
        writer.append("        repository.addObserver(link);\n");
        writer.append("    }\n");
        
        writer.append("    public static void removeObserver(xowl.interpreter.Repository repository, ClassObserver<" + cg.getName() + "> observer) {\n");
        writer.append("        ClassObserverLink link = mapClassObservers.get(observer);\n");
        writer.append("        repository.removeObserver(link);\n");
        writer.append("        mapClassObservers.remove(observer);\n");
        writer.append("    }\n");
        
        writer.append("    public xowl.lang.runtime.ObjectProperty getObjectProperty(String name) {\n");
        for (xowl.generator.PropertyImplementation impl : cg.getPropertyImplementations()) {
            if (!impl.getProperty().isObjectProperty())
                continue;
            String name = impl.getProperty().getName();
            writer.append("        if (name.equals(\"" + name + "\"))\n");
            writer.append("            return repository.interpretAsObjectProperty(repository.resolveEntity(getProperty_" + name + "()));\n");
        }
        writer.append("        return null;\n");
        writer.append("    }\n");
        
        writer.append("    public xowl.lang.runtime.DataProperty getDataProperty(String name) {\n");
        for (xowl.generator.PropertyImplementation impl : cg.getPropertyImplementations()) {
            if (impl.getProperty().isObjectProperty())
                continue;
            String name = impl.getProperty().getName();
            writer.append("        if (name.equals(\"" + name + "\"))\n");
            writer.append("            return repository.interpretAsDataProperty(repository.resolveEntity(getProperty_" + name + "()));\n");
        }
        writer.append("        return null;\n");
        writer.append("    }\n");
        
        writer.append("\n");
    }
}
